#define VC_EXTRALEAN
#pragma once

#include "Point.h"
#include "Global.h"
#include "GameText.h"
#include "Sprite.h"
#include "Character.h"

#include <d3d9.h>      // Main Direct3D header
#include <d3dx9.h>

#include <Windowsx.h>  // Get x/y LParam
#include <windows.h>

#include <sstream>     //ToString function ostringstream

//#include <WinError.h> help with error codes
//#include <stdio.h>  // IO operations
//#include <string>
//#include <time.h> // For time maths
//#pragma warning( disable : 4996 ) // disable deprecated warning 
//#include <strsafe.h> // For better String handling supported from xp sp2
//#pragma warning( default : 4996 )


LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam );
int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInst, LPSTR lpCmdLine, int cmdShow);

void CreateDevice(HWND hWnd);
void LoadGraphics();
void UpdateKeyboard(WPARAM key);
void UpdateMouse(LPARAM lParam);
void Render();
void RenderLights();
void Cleanup();
void UpdateFps();

using namespace std;

LPD3DXSPRITE         SpriteBatch       = NULL;

LPDIRECT3DDEVICE9    Device       = NULL;
LPDIRECT3D9          DeviceObject = NULL;

wstring              AppName      = L"Maple Story";

GameText			 *StatusText = NULL;

Point				 *MousePosition = NULL;
int                  KeyDown = 0; // 0=None

short                Fps = 0; // The one that shows last Fps
short                FpsCounter = 0;
DWORD                FpsTime = 0;

Sprite				 *Map = NULL;
Character            *Character1 = NULL;


//The First Function that executes in Initalizion
void CreateDevice(HWND hWnd)
{
	//Create DeviceObject, required for creating Device
	DeviceObject = Direct3DCreate9(D3D9b_SDK_VERSION);
	if (DeviceObject == NULL) throw "Could Not Create Device Object";

	//Make Parameters for the device
	D3DPRESENT_PARAMETERS Parameters;
	ZeroMemory(&Parameters, sizeof(Parameters));

	Parameters.Windowed = !FullScreen;
	Parameters.hDeviceWindow = hWnd;
	Parameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
	Parameters.BackBufferFormat = D3DFMT_X8R8G8B8;
	Parameters.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
	Parameters.EnableAutoDepthStencil = TRUE;
    Parameters.AutoDepthStencilFormat = D3DFMT_D16;
	Parameters.FullScreen_RefreshRateInHz = 0;

	if (FullScreen)
	{
		Parameters.BackBufferHeight = ScreenHeight;
		Parameters.BackBufferWidth = ScreenWidth;
	}
	else
	{
		Parameters.BackBufferHeight = WindowHeight;
		Parameters.BackBufferWidth = WindowWidth;
	}

	//Create Device
	HRESULT result = DeviceObject->CreateDevice(
			D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,hWnd, 
			D3DCREATE_HARDWARE_VERTEXPROCESSING, &Parameters, &Device);

	// Retry with software Vertexprocessing
	if (FAILED(result))
	{
		result = DeviceObject->CreateDevice(
			D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,hWnd, 
			D3DCREATE_SOFTWARE_VERTEXPROCESSING, &Parameters, &Device);

		if (FAILED(result)) throw "Could Not Create Device";
	}

    // Turn on the zbuffer
    HRESULT hr = Device->SetRenderState(D3DRS_ZENABLE, TRUE);
}


void LoadGraphics()
{
	D3DXCreateSprite(Device, &SpriteBatch);

	MousePosition = new Point(0,0);
	StatusText = new GameText(Device);
	Map = new Sprite();
	Character1 = new Character();

	Map->LoadSprite(Device, MaterialPath + L"Maps\\001010004.png");
	Map->SetPosition(0,0);
	StatusText->AddInfo("Loaded Start Map");

	Character1->LoadSprite(Device);
}

void UpdateKeyboard(WPARAM key)
{
	switch(key)
	{
		// Move
		case VK_UP:
			StatusText->AddInfo("Key 'Up' Was Pressed");
		break;

		case VK_DOWN:
			StatusText->AddInfo("Key 'Down' Was Pressed");
		break;

		case VK_RIGHT:
			StatusText->AddInfo("Key 'Right' Was Pressed");
		break;

		case VK_LEFT:
			StatusText->AddInfo("Key 'Left' Was Pressed ");
		break;
	}
}

void UpdateMouse(LPARAM lParam)
{
	Point CurrentMousePosition = Point(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
	Point movement = CurrentMousePosition.Sub(*MousePosition);
	StatusText->SetPosition(D3DXVECTOR2(CurrentMousePosition.X(), CurrentMousePosition.Y()));
}


void Render()
{
	UpdateFps();

	Device->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(100,149,237),1.0f,0);

	Device->BeginScene();

	SpriteBatch->Begin(D3DXSPRITE_ALPHABLEND);

		Map->Render(SpriteBatch, 255);
		
		Device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_MIRROR);
		Device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_MIRROR);
		Character1->Render(SpriteBatch);

		StatusText->Draw(SpriteBatch);

	SpriteBatch->End();
	
	Device->EndScene();

	Device->Present(0,0,0,0);
}

void UpdateFps()
{
	DWORD CurrentTime = GetTickCount();
	FpsCounter++;

	if ((CurrentTime - FpsTime) >= 1000)
	{
		Fps = FpsCounter;
		StatusText->SetFps(FpsCounter);
		FpsTime = CurrentTime;
		FpsCounter = 0; 
	}
}


void Cleanup()
{
	StatusText->Release();
	SpriteBatch->Release();

	delete StatusText;
	delete MousePosition;
	delete Map;
	delete Character1;

	SpriteBatch = NULL;
	StatusText = NULL;
	MousePosition = NULL;
	Map = NULL;
	Character1 = NULL;

	Device->Release();
	Device = NULL;

	DeviceObject->Release();
	DeviceObject = NULL;
}



int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInst, LPSTR lpCmdLine, int cmdShow)
{
	WNDCLASSEX wc = {0};

    wc.cbSize        = sizeof(WNDCLASSEX);
    wc.style         = 0;
    wc.lpfnWndProc   = MsgProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = hInstance;
    wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = AppName.c_str();
    wc.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);
	if (FullScreen)
		wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);

	RegisterClassEx(&wc);

	DWORD WindowStyle = WS_OVERLAPPEDWINDOW;

	if (FullScreen)
		WindowStyle = WS_EX_TOPMOST | WS_POPUP;

	HWND hWnd = CreateWindow(
		AppName.c_str(), AppName.c_str(), WindowStyle, 10, 10, WindowWidth, WindowHeight, NULL, NULL, hInstance, NULL); 

	try
	{
		CreateDevice(hWnd);
	}
	catch(int ErrorNumber)
	{
		MessageBoxA(hWnd, (ToString(ErrorNumber)).c_str(), "Error in Create Device Function", 0);
	}
	catch(...)
	{
		MessageBoxA(hWnd, "Unknown Error in Create Device Function", "Error in Create Device Function", 0);
	}
	
	try
	{
		LoadGraphics();
	}
	catch(int ErrorNumber)
	{
		MessageBoxA(hWnd, (ToString(ErrorNumber)).c_str(), "Error in LoadGraphics Function", 0);
	}
	catch(...)
	{
		MessageBoxA(hWnd, "Unknown Error in LoadGraphics Function", "Error in LoadGraphics Function", 0);
	}

	ShowWindow(hWnd, SW_SHOWDEFAULT);
	UpdateWindow(hWnd);


	// Enter the message loop
    MSG msg;
    ZeroMemory( &msg, sizeof( msg ) );
    while( msg.message != WM_QUIT )
    {
        if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
        {
            TranslateMessage( &msg );
            DispatchMessage( &msg );
        }
        else
			try
			{
				Render();
			}
			catch(int ErrorNumber)
			{
				MessageBoxA(hWnd, (ToString(ErrorNumber)).c_str(), "Error in Render Loop", 0);
			}
			catch(...)
			{
				MessageBoxA(hWnd, "Unknown Error in Render Loop", "Error in Render Loop", 0);
			}
    }
}


//Message Handler
LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
    switch( msg )
    {
        case WM_DESTROY:
            Cleanup();
            PostQuitMessage(0);
            return 0;

			case WM_KEYDOWN:
				UpdateKeyboard(wParam);
			break;

			case WM_MOUSEMOVE:
				UpdateMouse(lParam);
			break;
    }

    return DefWindowProc( hWnd, msg, wParam, lParam );

};